package pl.matt.media.extractor;

import javax.media.ConfigureCompleteEvent;
import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.media.EndOfMediaEvent;
import javax.media.Format;
import javax.media.IncompatibleSourceException;
import javax.media.Manager;
import javax.media.MediaLocator;
import javax.media.PrefetchCompleteEvent;
import javax.media.Processor;
import javax.media.RealizeCompleteEvent;
import javax.media.ResourceUnavailableEvent;
import javax.media.ResourceUnavailableException;
import javax.media.StopAtTimeEvent;
import javax.media.control.TrackControl;
import javax.media.format.RGBFormat;
import javax.media.format.VideoFormat;
import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.DataSource;

import pl.matt.media.protocol.JPEGDataSourceHandler;
import pl.matt.media.strategy.FrameNamingStrategy;

/**
 * @author mateusz
 * 
 */
public class JMFFramesExtractor implements ControllerListener {

	private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(JMFFramesExtractor.class);

	private Object waitObj = new Object();
	private boolean stateOK = true;
	private String locatorString;
	private FrameNamingStrategy frameNamingStrategy;

	public void extract() {
		//String locatorString = "file:" + videoFilePath;
		MediaLocator ml = new MediaLocator(locatorString);
		Processor p;
		try {
			p = Manager.createProcessor(ml);
		} catch (Exception e) {
			e.printStackTrace();
			return;
		}

		p.addControllerListener(this);

		// Put the Processor into configured state.
		p.configure();
		if (!waitForState(Processor.Configured, p)) {
			return;
		}
		// Get the raw output from the Processor.
		p.setContentDescriptor(new ContentDescriptor(ContentDescriptor.RAW));

		TrackControl tc[] = p.getTrackControls();
		if (tc == null) {
			return;
		}
		TrackControl videoTrack = null;
		for (int i = 0; i < tc.length; i++) {
			System.out.println(tc[i].getFormat());
			if (tc[i].getFormat() instanceof VideoFormat) {
				VideoFormat videoFormat = (VideoFormat) tc[i].getFormat();
				tc[i].setFormat(new RGBFormat(videoFormat.getSize(), -1, Format.byteArray, -1.0F, 24, 3, 2, 1));
				videoTrack = tc[i];
			} else {
				tc[i].setEnabled(false);
			}
		}
		if (videoTrack == null) {
			LOG.info("The input media does not contain a video track.");
			return;
		}

		p.realize();
		if (!waitForState(Processor.Realized, p)) {
			LOG.info("Failed to realize the processor.");
			return;
		}

		// Get the output DataSource from the processor and set it to the
		// DataSourceHandler.
		DataSource ods = p.getDataOutput();
		JPEGDataSourceHandler handler;
		try {
			handler = new JPEGDataSourceHandler(frameNamingStrategy, (VideoFormat) videoTrack.getFormat());
			handler.setSource(ods); // also determines image size
			handler.start();
		} catch (ResourceUnavailableException e1) {
			e1.printStackTrace();
		} catch (IncompatibleSourceException e) {
			e.printStackTrace();
		}

		// Prefetch the processor.
		p.prefetch();

		if (!waitForState(Processor.Prefetched, p)) {
			LOG.info("Failed to prefetch the processor.");
			return;
		}

		p.start();
	}

	private boolean waitForState(int state, Processor p) {
		synchronized (waitObj) {
			try {
				while (p.getState() < state && stateOK) {
					waitObj.wait();
				}
			} catch (Exception e) {
			}
		}
		return stateOK;
	}

	public void controllerUpdate(ControllerEvent evt) {
		if (evt instanceof ConfigureCompleteEvent || evt instanceof RealizeCompleteEvent
				|| evt instanceof PrefetchCompleteEvent) {
			synchronized (waitObj) {
				stateOK = true;
				waitObj.notifyAll();
			}
		} else if (evt instanceof ResourceUnavailableEvent) {
			synchronized (waitObj) {
				stateOK = false;
				waitObj.notifyAll();
			}
		} else if (evt instanceof EndOfMediaEvent || evt instanceof StopAtTimeEvent) {
			// tidyClose();
		}

	}

	public void setLocatorString(String locatorString) {
		this.locatorString = locatorString;
	}

	public void setFrameNamingStrategy(FrameNamingStrategy frameNamingStrategy) {
		this.frameNamingStrategy = frameNamingStrategy;
	}

}
